import os
import logging
from . import default
from .utils import get_default_manager
import re
import copy
import h5py
import numpy as np
# __all__ = ['Manager']


class Manager(object):
    def __init__(self):
        self.dir_set = False
        self.__instance_name_dict = {}  # name -> instance的映射
        self.__instance_name_list = []  # 按加入时间顺序存储名字。两个要同时维护

    def __enter__(self):
        self.__previous_default_manager = get_default_manager()
        default.manager = self
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        default.manager = self.__previous_default_manager
        if exc_tb is not None:
            return False
        else:
            return True

    def __getitem__(self, name):
        return self.get_instance(name)

    def set_dir(self, pj_root, data_dir=None):
        assert self.dir_set is False, "Can't set dir again!"
        self.pj_root = pj_root
        if data_dir is None:
            self.data_dir = os.path.join(pj_root, 'data')
        else:
            self.data_dir = data_dir
        if os.path.exists(self.pj_root) is False:
            raise Exception('Can\'t find pj_root!')
        if os.path.exists(self.data_dir) is False:
            logging.warning('Data_dir doesn\'t exist! Now create %s' % self.data_dir)
            os.makedirs(self.data_dir)
        self.dir_set = True

    def add_instance(self, instance):
        assert instance.manager == self
        if instance.name is not None:
            assert instance.name not in self.__instance_name_dict, "Duplicated name for instance: %s" % (instance.name)
            self.__instance_name_dict[instance.name] = instance
            self.__instance_name_list.append(instance.name)

    def get_no_duplicated_name(self, name):
        origin_name = name
        name = str(default.scope) + name
        i = 0
        while ('%s-%d' % (name, i)) in self.__instance_name_dict:
            i += 1
        return '%s-%d' % (origin_name, i)

    def get_instance(self, name):
        assert name in self.__instance_name_dict, "Can't find instance for name: %s" % (name)
        return self.__instance_name_dict[name]

    def regex_select_instance(self, pattern):
        result = []
        m = re.compile(pattern)
        for name in self.__instance_name_list:
            if m.match(name):
                result.append(name)
        return result

    def list_instance(self):
        return copy.copy(self.__instance_name_list)

    def reset_instance(self):
        self.__instance_name_dict = {}
        self.__instance_name_list = []

    def reset_all(self):
        self.dir_set = False
        self.reset_instance()

    def load_and_save_to_hdf5(self, names, hdf5_name='data.hdf5', mode='a', verbose=True, save_columns=False):
        '''
        names是name的一个list。names中所有的instance必须实现计算并保存好。
        这个函数的功能是把这些instanceload进来并且存到一个hdf5里面。这个hdf5文件就在data_dir里面。
        默认不保存columns和index。也可以设置保存，这时columns会自动被转换为str（astype('S')）。在python3中可能需要astype(str)转换回来。
        不提供保存index，为啥？因为这个metadata有64K的限制。。
        '''

        def __get_all_datasets(f):
            all_datasets = []
            def __func(name, y):
                if isinstance(y, h5py.Dataset):  # 也会访问到group
                    all_datasets.append(name)
            f.visititems(__func)
            return all_datasets

        assert self.dir_set
        f = h5py.File(os.path.join(self.data_dir, hdf5_name), mode)
        exists_datasets = __get_all_datasets(f)
        if verbose:
            print('mode: %s' % (mode))
        for name in names:
            df = self.get_instance(name).load()
            values = df.values
            if name in exists_datasets:
                print('WARNING: %s already in hdf5, will override it...' % name)
                dataset = f[name]
                dataset[...] = values
            else:
                dataset = f.create_dataset(name, data=values)
            if save_columns:
                dataset.attrs['columns'] = np.array(df.columns.tolist()).astype('S')
            if verbose:
                print('%s saved...' % name)
        f.close()
        if verbose:
            print('completed. check: %s' % (os.path.join(self.data_dir, hdf5_name)))


default.manager = Manager()


def set_dir(pj_root, data_dir=None):
    manager = get_default_manager()
    return manager.set_dir(pj_root, data_dir)


def add_instance(instance):
    manager = get_default_manager()
    return manager.add_instance(instance)


def get_no_duplicated_name(name):
    manager = get_default_manager()
    return manager.add_instance(name)


def get_instance(name):
    manager = get_default_manager()
    return manager.get_instance(name)


def regex_select_instance(pattern):
    manager = get_default_manager()
    return manager.regex_select_instance(pattern)


def list_instance():
    manager = get_default_manager()
    return manager.list_instance()


def reset_instance():
    manager = get_default_manager()
    return manager.reset_instance()


def reset_all():
    manager = get_default_manager()
    return manager.reset_all()


def load_and_save_to_hdf5(names, hdf5_name='data.hdf5', mode='a', verbose=True):
    manager = get_default_manager()
    return manager.load_and_save_to_hdf5(names, hdf5_name, mode, verbose)
